package com.yansu.own.schedule;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.yansu.own.client.*;
import com.yansu.own.common.CustomException;
import com.yansu.own.entity.pojo.SysAppContent;
import com.yansu.own.entity.pojo.SysUserContent;
import com.yansu.own.mapper.AppFocusMediaMapper;
import com.yansu.own.mapper.SysAppContentMapper;
import com.yansu.own.mapper.SysUserContentMapper;
import com.yansu.own.schedule.dingding.*;
import com.yansu.own.util.RedisLockUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
 * @Author: LongMen
 * @Date: 2023/1/12
 * @Description: 钉钉订阅号文章同步接口
 */
//@EnableScheduling
//@Configuration
@RequestMapping("/test")
@RestController
public class ArticleSynchronizationSchedule {

    @Autowired
    private DingDingClient dingClient;

    @Autowired
    private SysAppContentMapper contentMapper;

    @Value("${dingDing.AppKey}")
    private String AppKey;

    @Value("${dingDing.AppSecret}")
    private String AppSecret;

    @Value("${dingDing.unionId}")
    private String unionid;

    private String access_token;

    private Integer mediaId;


    @Autowired
    private RedisLockUtil lockUtil;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysUserContentMapper userContentMapper;

    @Autowired
    private AppFocusMediaMapper mediaMapper;

    private Integer insertCount = 0;

    private Integer editCount = 0;

    /**
     * 缓存数据锁
     */
    public static final String SYNC_TYPE = "sync_dingDingArticle";

    /**
     * 同步任务分布式锁
     */
    public static final String lock = "system:schedule:syncDingDingArticle";

    /**
     * 同步中标识
     */
    public static final String SYNC_ING = "ing";

    /**
     * 同步完成或未同步
     */
    public static final String SYNC_END = "end";

    public Integer getMediaId() {
        return mediaId;
    }

    public void setMediaId() {
        Integer mediaId = mediaMapper.getMediaByName("产业研究院");
        this.mediaId = mediaId;
    }

    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("sync-dingDingArticle-pool-%d").build();

    private static final Logger log = LoggerFactory.getLogger(ArticleSynchronizationSchedule.class);

    public Integer getInsertCount() {
        return insertCount;
    }

    public void addInsertCount(Integer amount) {
        this.insertCount += amount;
    }

    public void initializeInsertCount() {
        this.insertCount = 0;
    }

    public Integer getEditCount() {
        return editCount;
    }

    public void addEditCount(Integer amount) {
        this.editCount += amount;
    }

    public void initializeEditCount() {
        this.editCount = 0;
    }

    public String getAccess_token() {
        return access_token;
    }

    public void setAccess_token() {
        String token = getToken();
        this.access_token = token;
    }

    @GetMapping("/test1")
    public void test1() {
        SysAppContent content1 = new SysAppContent();
        content1.setContent("1");
        content1.setSourceId(3879006L);
        SysAppContent content2 = new SysAppContent();
        content2.setContent("2");
        content2.setSourceId(3883007L);
        ArrayList<SysAppContent> list = new ArrayList<>();
        list.add(content1);
        list.add(content2);
        for (SysAppContent content : list) {
            contentMapper.dingDingUpdate(content);
        }
    }

    @GetMapping("/test2")
    public void test2() {
        Set keys = redisTemplate.keys("dingArticleId:*");
        for (Object key : keys) {
            redisTemplate.delete(key);
        }
    }

    @GetMapping("/test3")
    public void test3() {
        Set keys = redisTemplate.keys("dingArticleTitle:*");
        for (Object key : keys) {
            redisTemplate.delete(key);
        }
    }

    //@Scheduled(cron = "0 0 0 * * ?")
    @GetMapping("/test")
    public void articleSynchronization() {
        //打印任务开始
        log.info("Sync DingDing Article Task Starts");
        //获取本次定时任务token
        setAccess_token();
        //获取产业研究院id
        setMediaId();
        //初始化新增和修改条数
        initializeInsertCount();
        initializeEditCount();
        long time = System.currentTimeMillis();
        int syncedCount = 0;
        try {
            if (!lockUtil.tryLock(lock, String.valueOf(time), 60)) {
                return;
            }
            redisTemplate.expire(lock, 30, TimeUnit.SECONDS);
            //初始化缓存数据状态
            initDataState();
            //查询订阅号下的文章数量
            int totalCount = getArticleTotal();
            int size = 20;
            int totalPage = totalCount / size + ((totalCount % size) == 0 ? 0 : 1);
            if (totalPage == 0) {
                redisTemplate.opsForHash().put(SYNC_TYPE, "status", SYNC_END);
            } else {
                redisTemplate.opsForHash().put(SYNC_TYPE, "totalPage", totalPage);
                redisTemplate.opsForHash().put(SYNC_TYPE, "totalCount", totalCount);
                ExecutorService executorService = new ThreadPoolExecutor(10, 10,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<>(10240), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
                List<Future<Integer>> results = new ArrayList<>();
                for (int i = 1; i <= totalPage; i++) {
                    Future<Integer> submit = executorService.submit(new SyncDingDingRunnable(redisTemplate, this, i, size));
                    results.add(submit);
                }
                executorService.shutdown();
                //半小时
                boolean termination = executorService.awaitTermination(30, TimeUnit.MINUTES);
                if (termination) {
                    for (int i = 0; i < results.size(); i++) {
                        syncedCount += results.get(i).get();
                    }
                }
            }
        } catch (Exception e) {
            log.error("同步钉钉产业研究院订阅号文章异常：", e);
            lockUtil.unlock(lock, String.valueOf(time));
        } finally {
            redisTemplate.opsForHash().put(SYNC_TYPE, "status", SYNC_END);
            Map<Object, Object> syncHelper = redisTemplate.opsForHash().entries(SYNC_TYPE);
            int duration = (int) syncHelper.get("syncedTime");
            log.info("钉钉产业研究院订阅号文章同步完成，共更新数据量(新增+修改)为：" + syncedCount + "耗时：" + duration / 1000 + "秒");
            //任务结束提醒
            long time2 = System.currentTimeMillis();
            log.info("spend time " + (time2 - time) + "ms");
            log.info("此次钉钉新增数据" + getInsertCount() + "条");
            log.info("此次钉钉修改数据" + getEditCount() + "条");
            log.info("Sync DingDing Article Task End");
        }
    }

    /**
     * 获取钉钉access_token
     */
    public String getToken() {
        DingDingResponse getToken = dingClient.gettoken(AppKey, AppSecret);
        return getToken.getAccess_token();
    }

    /**
     * 获取产业研究院的unionid
     */
    public String subscriptionList() {
        DIngDingPage page = new DIngDingPage();
        page.setPageSize(1);
        page.setPageSize(50);
        DingDingResponse2<DingDIngSubscription> response2;
        response2 = dingClient.subscriptionList(getToken(), page);
        if (response2.getItem_count() != 0) {
            List<DingDIngSubscription> items = response2.getItems();
            for (DingDIngSubscription item : items) {
                if ("产业研究院".equals(item.getName())) {
                    System.out.println(item.getUnionid());
                    return item.getUnionid();
                }
            }
        }
        for (int i = 1; i < 3; i++) {
            page.setPageSize(page.getPageSize() + i);
            response2 = dingClient.subscriptionList(getToken(), page);
            if (response2.getItem_count() != 0) {
                List<DingDIngSubscription> items = response2.getItems();
                for (DingDIngSubscription item : items) {
                    if ("产业研究院".equals(item.getName())) {
                        return item.getUnionid();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取产业研究院指定文章
     */
    public int saveDingDingArticle(Integer pageNum, Integer pageSize) {
        long start = System.currentTimeMillis();
        //请求指定页数的文章
        DIngDingPage2 page = new DIngDingPage2();
        page.setPage_size(pageSize);
        page.setPage_start(pageNum);
        page.setUnionid(unionid);
        DingDingResponse2<DingDingArticle> response2 = dingClient.articleList(getAccess_token(), page);
        //变量初始化
        DingDingArticleDto dto;
        SysAppContent content;
        SysUserContent userContent;
        DingDingArticleResponse dingArticle;
        int change;
        int addCount = 0;
        int updateCount = 0;
        if (response2.getItem_count() != 0) {
            List<DingDingArticle> items = response2.getItems();
            for (DingDingArticle article : items) {
                if (article.getPublish_status() == 1 && article.getPublish_time() != null) {
                    //请求详情页接口(钉钉列表接口内容不提供content字段)
                    dto = new DingDingArticleDto();
                    dto.setArticle_id(article.getArticle_id());
                    dto.setUnionid(unionid);
                    dingArticle = dingClient.articleDetails(getAccess_token(), dto);
                    //钉钉返回文章转换为数据库文章
                    content = new SysAppContent();
                    content.setMediaId(getMediaId()); //设置产业研究院id
                    content.setPictureEnlarge(1); //默认图片可放大
                    content.setSourceId(dingArticle.getArticle_id());
                    content.setTitle(dingArticle.getTitle().trim());
                    //content.setCoverUrl(dingArticle.getThumb_media_id());
                    content.setContentState(dingArticle.getPublish_status());
                    content.setContent(dingArticle.getContent());
                    content.setContentUrl(dingArticle.getUrl());
                    content.setPublishTime(timeTransition(dingArticle.getPublish_time()));
                    content.setCreateTime(timeTransition(dingArticle.getCreate_time()));
                    content.setUpdateTime(timeTransition(dingArticle.getUpdate_time()));
                    content.setSyncTime(new Date());
                    content.setIsDeleted(0);
                    if (!checkExist(article.getTitle().trim())) {
                        contentMapper.dingInsertContent(content);
                        //写入关联表数据
                        userContent = new SysUserContent();
                        userContent.setUserId(0L);
                        userContent.setContentId(content.getId());
                        userContentMapper.addUserContent(userContent);
                        //第一次写入缓存
                        redisTemplate.opsForValue().set("dingArticleTitle:" + content.getTitle(), "exist");
                        addCount++;
                    } else {
                        //根据标题判断数据库是否有在此更新时间后的数据
                        int i = contentMapper.selectByTitleAndUpdateTime(content.getTitle(), content.getUpdateTime(), content.getSourceId());
                        if (i == 0) {
                            //覆盖原articleId的数据
                            change = contentMapper.dingDingUpdate(content);
                            if (change > 0) {
                                updateCount++;
                            }
                        } else {
                            //覆盖标题相同的数据
                            change = contentMapper.dingUpdateByTitle(content);
                            if (change > 0) {
                                updateCount++;
                            }
                        }
                    }
                }
            }
        }
        long time = System.currentTimeMillis() - start;
        log.info("钉钉同步订阅号文章，第{}页新增{}条数据", pageNum, addCount);
        log.info("钉钉同步订阅号文章，第{}页修改{}条数据", pageNum, updateCount);
        log.info("第{}页耗时：{}ms", pageNum, time);
        addEditCount(updateCount);
        addInsertCount(addCount);
        return addCount + updateCount;
    }

    /**
     * 检查数据库是否已存在钉钉订阅号文章
     */
    private boolean checkExist(String title) {
        boolean flag = false;
        String data = (String) redisTemplate.opsForValue().get("dingArticleTitle:" + title);
        if (data != null) {
            flag = true;
        } else {
            int i = contentMapper.selectByTitle(title,null);
            if (i > 0) {
                flag = true;
                redisTemplate.opsForValue().set("dingArticleTitle:" + title, "exist");
            }
        }
        return flag;
    }

    /**
     * 获取产业研究院订阅号下文章总数
     */
    public int getArticleTotal() {
        DIngDingPage2 page = new DIngDingPage2();
        page.setPage_size(1);
        page.setPage_start(1);
        page.setUnionid(unionid);
        SysAppContent content;
        DingDingResponse2<DingDingArticle> response2;
        response2 = dingClient.articleList(getAccess_token(), page);
        return response2.getTotal_count();
    }

    /**
     * 通过文章id获取文章详情
     */
    private SysAppContent getArticleDetail(Long articleId) {
        DingDingArticleDto dto = new DingDingArticleDto();
        dto.setArticle_id(articleId);
        dto.setUnionid(unionid);
        return contentTransition(dingClient.articleDetails(getToken(), dto));
    }

    /**
     * 钉钉文章转换为数据库文章
     */
    private SysAppContent contentTransition(DingDingArticle dingArticle) {
        SysAppContent content = new SysAppContent();
        //设置产业研究院id
        content.setMediaId(119);
        //默认图片可放大
        content.setPictureEnlarge(1);
        //封装其他属性
        content.setSourceId(dingArticle.getArticle_id());
        content.setTitle(dingArticle.getTitle());
        content.setCoverUrl(dingArticle.getThumb_media_id());
        content.setContentState(dingArticle.getPublish_status());
        content.setContent(dingArticle.getContent());
        content.setContentUrl(dingArticle.getUrl());
        content.setPublishTime(timeTransition(dingArticle.getPublish_time()));
        content.setCreateTime(timeTransition(dingArticle.getCreate_time()));
        content.setUpdateTime(timeTransition(dingArticle.getUpdate_time()));
        content.setSyncTime(new Date());
        content.setIsDeleted(0);
        return content;
    }

    /**
     * 时间戳转日期
     */
    private Date timeTransition(Long time) {
        if (time != null) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return new Date(time);
        } else {
            return null;
        }
    }

    /**
     * 初始化缓存
     */
    private void initDataState() {
        Map<Object, Object> syncHelper = redisTemplate.opsForHash().entries(SYNC_TYPE);
        if (syncHelper != null && syncHelper.size() > 0) {
            //syncHelper不为null的时候，信任里面的status，syncedCount，latestSyncTime totalPage syncedPage数据是存在
            String status = (String) syncHelper.get("status");
            if (StringUtils.isNotBlank(status) && status.equals(SYNC_ING)) {
                //状态为同步中,如果出现异常，有可能同步状态无法正确修改，可通过
                Integer syncedCount = (Integer) syncHelper.get("syncedCount");
                throw new CustomException(MessageFormat.format("数据同步中，已同步{0}条数据，请稍后查看同步结果。", syncedCount));
            }
        }
        syncHelper = new HashMap<>();
        syncHelper.put("totalPage", 0);
        syncHelper.put("totalCount", 0);
        syncHelper.put("syncedPage", 0);
        syncHelper.put("syncedCount", 0);
        syncHelper.put("syncedTime", 0);
        syncHelper.put("status", SYNC_ING);
        redisTemplate.opsForHash().putAll(SYNC_TYPE, syncHelper);
        redisTemplate.expire(SYNC_TYPE, 3600, TimeUnit.SECONDS);
    }
}
